package com.wn.common.net.config

import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.EncryptUtils
import com.blankj.utilcode.util.NetworkUtils
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.wn.common.utils.CnSpUtils
import okhttp3.*
import okio.Buffer
import java.lang.reflect.Type

/**
 * @Author: wn
 * @Date: 2023/1/4 16:24
 * @Description: 公共 header的拦截器
 */
class CnInterceptor : Interceptor {
  companion object {
    //    复杂数据类型
    private val gson: Gson = GsonBuilder().enableComplexMapKeySerialization().create()
    private val mapType: Type = object : TypeToken<Map<String, Any>>() {}.type
  }

  override fun intercept(chain: Interceptor.Chain): Response {
    val originRequest: Request = chain.request()

    /**
     * 附加的公共headers，封装clientInfo，deviceInfo等，也可以在post请求中，自定义封装headers的字段内容
     * 注意这里，服务器用于校检的字段，只能是以下的字段内容，可以缺失，但是不能额外添加，因为服务器未做处理
     */
    val attachHeaders = mutableListOf(
      "appid" to NET_CONFIG_APP_ID,
      "platform" to "android",// 如果重复请求，可能会报重复签名错误
      "timestamp" to System.currentTimeMillis().toString(),
      "brand" to DeviceUtils.getManufacturer(),// 制造商
      "model" to DeviceUtils.getModel(),// 型号
      "uuid" to DeviceUtils.getUniqueDeviceId(),// 唯一设备Id
      "network" to NetworkUtils.getNetworkType().name,// 网络
      "system" to DeviceUtils.getSDKVersionName(),// 版本号
      "version" to AppUtils.getAppVersionName()  // 版本名
    )
    // token仅在有值的时候才传递
//  suspend fun getUserInfo(@Header("token") token: String?)
//    val localToken = originRequest.header("token") ?: ""
    val localToken = CnSpUtils.getString(SP_KEY_USER_TOKEN, originRequest.header("token")) ?: ""
    if (localToken.isNotEmpty()) {
      attachHeaders.add("token" to localToken)
    }
    // 需要加密的请求参数 公共请求参数 加上 之前原本的请求参数
    val signHeaders = mutableListOf<Pair<String, String>>()
    signHeaders.addAll(attachHeaders)
    // get的请求，参数
    if (originRequest.method == "GET") {
      originRequest.url.queryParameterNames.forEach {
        // 将之前原本的 参数追加到  标志头 中
        signHeaders.add(it to (originRequest.url.queryParameter(it) ?: ""))
      }
    }
    // post的请求 formBody形式，或 json形式的，都需要将内部的字段，遍历出来，参与 sign 的计算
    val requestBody = originRequest.body
    if (originRequest.method == "POST") {
      // formBody
      if (requestBody is FormBody) {
        for (i in 0 until requestBody.size) {
          // 将之前的 参数追加到  标志头 中
          signHeaders.add(requestBody.name(i) to requestBody.value(i))
        }
      }
      // json 的body 需要将requestBody反序列化为json转为map application/json
      if (requestBody?.contentType()?.type == "application" && requestBody.contentType()?.subtype == "json") {
        kotlin.runCatching {
          val buffer = Buffer()
          requestBody.writeTo(buffer)
          buffer.readByteString().utf8()
        }
          .onSuccess {
            val map = gson.fromJson<Map<String, Any>>(it, mapType)
            map.forEach { entry ->
              // FIXME:2020/12/28 value 目前json单层级
              signHeaders.add(entry.key to entry.value.toString())
            }
          }
        /* .onFailure {

         }*/
      }
    }
    // TODO 算法：都必须是非空参数！！！ sign=MD5(ascii排序后的headers及params的key=value拼接&后，最后拼接  appkey和value) 32位大写
    // TODO 这个地方 貌似应该是 signHeaders(attachHeaders 改为 signHeaders)
    // 加密的请求参数按照Ascii排序 用&拼接加上"&appkey=appKeyValue"
    val signValue = signHeaders
      .sortedBy { it.first }
      .joinToString("&") { "${it.first}=${it.second}" } // name=ww&age=18&hobby=eat
      .plus("&appkey=$NET_CONFIG_APP_KEY")

    // CacheControl.FORCE_NETWORK  FORCE_CACHE
    val newBuilder = originRequest.newBuilder().cacheControl(CacheControl.FORCE_NETWORK)
    // newBuilder 追加 所有的 封装好的 headers
    attachHeaders.forEach {
      newBuilder.header(it.first, it.second)
    }
    // 添加签名信息
    newBuilder.header("sign", EncryptUtils.encryptMD5ToString(signValue))// 返回 MD5 加密的字符串
    // 之前的 post  get signHeaders 写了个寂寞么
    if (originRequest.method == "POST" && requestBody != null) {
      newBuilder.post(requestBody)
    }
    if (originRequest.method == "GET") {
      newBuilder.get()
    }
    return chain.proceed(newBuilder.build())
  }
}